package org.gbif.crawler.ws.mock;

import org.gbif.crawler.ws.DatasetProcessService;
import org.gbif.crawler.ws.DatasetProcessStatus;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Sets;
import com.google.common.io.Closeables;

/**
 * Reads a file of "stuff" on startup and then randomly updates stuff to simulate data change.
 */
public class MockDatasetProcessService implements DatasetProcessService {

  // multiple views on the same objects
  private static final List<DatasetProcessStatus> running;
  private static final List<DatasetProcessStatus> queued;
  private static final List<DatasetProcessStatus> all;
  private static final Map<UUID, DatasetProcessStatus> index;

  static {
    ImmutableList.Builder<DatasetProcessStatus> ab = ImmutableList.builder();
    ImmutableList.Builder<DatasetProcessStatus> br = ImmutableList.builder();
    ImmutableList.Builder<DatasetProcessStatus> bq = ImmutableList.builder();
    ImmutableMap.Builder<UUID, DatasetProcessStatus> ib = ImmutableMap.builder();

    // there are duplicates
    Set<UUID> processed = Sets.newHashSet();
    int count = 0;
    for (DatasetProcessStatus dps : readMockEndpoints()) {
      if (!processed.contains(dps.getDatasetKey())) {
        processed.add(dps.getDatasetKey());
        ab.add(dps); // add to all
        ib.put(dps.getDatasetKey(), dps); // add to index
        // approx 10% go into running, the rest on the queue
        if (count++ % 10 == 0) {
          br.add(dps);
        } else {
          bq.add(dps);
        }
      }
    }
    all = ab.build();
    index = ib.build();
    running = br.build();
    queued = bq.build();
    new Thread(new Updater(running)).start();
  }

  /**
   * Randomly makes updates to the running group
   */
  private static class Updater implements Runnable {

    private final List<DatasetProcessStatus> running;
    // random options
    private final String[] statuses = {"Initializing", "Aaa-Aba", "Ccc-Cda", "Timeout"};
    private final Random randomGenerator = new Random();

    public Updater(List<DatasetProcessStatus> running) {
      this.running = running;
    }

    @Override
    public void run() {
      while (true) {
        for (DatasetProcessStatus dps : running) {
          dps.setStatus(statuses[randomGenerator.nextInt(statuses.length)]);
          dps.incremenFragmentsGenerared(randomGenerator.nextInt(3));
          dps.incrementErrors(randomGenerator.nextInt(3));
          dps.incrementPagesCrawled(randomGenerator.nextInt(3));
          dps.incrementPagesFragmented(randomGenerator.nextInt(3));
          dps.incrementFragmentsPersisted(randomGenerator.nextInt(3));
          dps.incrementRecordsInterpreted(randomGenerator.nextInt(3));
          dps.incrementRecordsCreated(randomGenerator.nextInt(3));
        }
        try {
          TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
          break;
        }
      }
    }
  };

  private static List<DatasetProcessStatus> readMockEndpoints() {
    ImmutableList.Builder<DatasetProcessStatus> b = ImmutableList.builder();
    BufferedReader r =
      new BufferedReader(new InputStreamReader(MockCrawlerDetailService.class.getResourceAsStream("/endpoints.txt")));
    try {
      Pattern tab = Pattern.compile("\t");
      String line = r.readLine();

      while (line != null) {
        String[] atoms = tab.split(line);
        b.add(DatasetProcessStatus.of(atoms[3], atoms[1], atoms[2], atoms[0]));
        line = r.readLine();
      }
    } catch (Exception e) {
      e.printStackTrace(); // we're hosed
    } finally {
      Closeables.closeQuietly(r);
    }
    return b.build();
  }

  @Override
  public DatasetProcessStatus getDatasetProcessStatus(UUID datasetKey) {
    return index.get(datasetKey);
  }

  @Override
  public List<DatasetProcessStatus> listAllDatasetProcesses() {
    return all;
  }

  @Override
  public List<DatasetProcessStatus> listRunningDatasetProcesses() {
    return running;
  }

  @Override
  public List<DatasetProcessStatus> listQueuedDatasetProcesses() {
    return queued;
  }
}
